package puzzle;

import java.util.*;

import aima.core.agent.Action;
import aima.core.agent.impl.DynamicAction;

public class PuzzleBoard {
    private int[] board;

    public static Action N_DER = new DynamicAction("N der");

    public static Action N_IZQ = new DynamicAction("N izq");

    public static Action O_DER = new DynamicAction("O der");

    public static Action O_IZQ = new DynamicAction("O izq");

    public static Action S_DER = new DynamicAction("S der");

    public static Action S_IZQ = new DynamicAction("S izq");

    public static Action E_DER = new DynamicAction("E der");

    public static Action E_IZQ = new DynamicAction("E izq");

    public static Action C_DER = new DynamicAction("C der");

    public static Action C_IZQ = new DynamicAction("C izq");

    public int[] getBoard() {
        return board;
    }

    public PuzzleBoard(int[] pieces) {
        board = pieces;
    }

    @Override
    public String toString() {
        String retVal = "   " + board[0] + " " + board[1] + "\n" + board[2]
                + " " + board[3] + " " + board[4] + " " + board[5] + " " + "\n"
                + board[6] + " " + board[7] + " " + board[8] + " " + board[9]
                + " " + "\n" + "   " + board[10] + " " + board[11];
        return retVal;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if ((o == null) || (this.getClass() != o.getClass())) {
            return false;
        }
        PuzzleBoard aBoard = (PuzzleBoard) o;

        for (Integer i = 0; i < 12; i++) {
            if (this.board[i] != aBoard.getBoard()[i]) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = 0;
        for (int i = 0; i < 12; i++) {
            result += i * board[i];
        }
        return result;
    }

    public int getMismatchValue() {
        int heuristicValue = 0;

        for (int i = 0; i < 12; i++) {
            heuristicValue += board[i] == i ? 0 : 1;
        }

        return heuristicValue;
    }

    public int getManhattanValue() {
        int heuristicValue = 0;

        for (int i = 0; i < 12; i++) {
            PuzzleBoardPossition currentPossition = new PuzzleBoardPossition(i);
            PuzzleBoardPossition objectivePossition = new PuzzleBoardPossition(
                    board[i]);
            heuristicValue += currentPossition.getDistance(objectivePossition);
        }

        return heuristicValue;
    }

    public int getCasesValue() {

        int heuristicValue = getCornersSwappedValue();
        if (heuristicValue > 0) {
            return heuristicValue;
        }

        return getCornersFrontedValue();
    }

    private int getCornersFrontedValue() {
        int value = 0;

        if (areMoved(0, 1, 3, 4)) {
            value += 3;
        }

        if (areMoved(5, 9, 4, 8)) {
            value += 3;
        }
        if (areMoved(11, 10, 8, 7)) {
            value += 3;
        }

        if (areMoved(2, 6, 3, 7)) {
            value += 3;
        }

        if (areMoved(0, 3, 1, 4)) {
            value += 3;
        }

        if (areMoved(4, 5, 8, 9)) {
            value += 3;
        }

        if (areMoved(7, 10, 8, 11)) {
            value += 3;
        }

        if (areMoved(2, 3, 6, 7)) {
            value += 3;
        }

        return value;
    }

    private int getCornersSwappedValue() {

        int value = 0;

        if (areSwapped(0, 1)) {
            value += 4;
        }

        if (areSwapped(5, 9)) {
            value += 4;
        }
        if (areSwapped(11, 10)) {
            value += 4;
        }

        if (areSwapped(2, 6)) {
            value += 4;
        }

        if (areSwapped(0, 3)) {
            value += 4;
        }

        if (areSwapped(1, 4)) {
            value += 4;
        }
        if (areSwapped(4, 5)) {
            value += 4;
        }

        if (areSwapped(8, 9)) {
            value += 4;
        }

        if (areSwapped(11, 8)) {
            value += 4;
        }

        if (areSwapped(10, 7)) {
            value += 4;
        }
        if (areSwapped(6, 7)) {
            value += 4;
        }

        if (areSwapped(2, 3)) {
            value += 4;
        }

        return value;
    }

    private boolean areSwapped(int i, int j) {
        return board[i] == j && board[j] == i;
    }

    private boolean areMoved(int i, int j, int k, int l) {
        return (board[l] == j && board[k] == i)
                || (board[j] == l && board[i] == k);
    }

    public void moverCuadranteNorteDerecha() {
        List<Integer> cuadrante = GetCuadranteNorte();
        Collections.reverse(cuadrante);
        updateBoard(cuadrante);
    }

    public void moverCuadranteNorteIzquierda() {
        updateBoard(GetCuadranteNorte());
    }

    public void moverCuadranteEsteDerecha() {
        List<Integer> cuadrante = GetCuadranteEste();
        Collections.reverse(cuadrante);
        updateBoard(cuadrante);
    }

    public void moverCuadranteEsteIzquierda() {
        updateBoard(GetCuadranteEste());
    }

    public void moverCuadranteSurDerecha() {
        List<Integer> cuadrante = GetCuadranteSur();
        Collections.reverse(cuadrante);
        updateBoard(cuadrante);
    }

    public void moverCuadranteSurIzquierda() {
        updateBoard(GetCuadranteSur());
    }

    public void moverCuadranteOesteDerecha() {
        List<Integer> cuadrante = GetCuadranteOeste();
        Collections.reverse(cuadrante);
        updateBoard(cuadrante);
    }

    public void moverCuadranteOesteIzquierda() {
        updateBoard(GetCuadranteOeste());
    }

    public void moverCuadranteCentroDerecha() {
        List<Integer> cuadrante = GetCuadranteCentro();
        Collections.reverse(cuadrante);
        updateBoard(cuadrante);
    }

    public void moverCuadranteCentroIzquierda() {
        updateBoard(GetCuadranteCentro());
    }

    private static List<Integer> GetCuadranteNorte() {
        return Arrays.asList(0, 1, 4, 3);
    }

    private static List<Integer> GetCuadranteEste() {
        return Arrays.asList(4, 5, 9, 8);
    }

    private static List<Integer> GetCuadranteSur() {
        return Arrays.asList(7, 8, 11, 10);
    }

    private static List<Integer> GetCuadranteOeste() {
        return Arrays.asList(2, 3, 7, 6);
    }

    private static List<Integer> GetCuadranteCentro() {
        return Arrays.asList(3, 4, 8, 7);
    }

    private void updateBoard(List<Integer> cuadrante) {
        Integer value = board[cuadrante.get(0)];
        board[cuadrante.get(0)] = board[cuadrante.get(1)];
        board[cuadrante.get(1)] = board[cuadrante.get(2)];
        board[cuadrante.get(2)] = board[cuadrante.get(3)];
        board[cuadrante.get(3)] = value;
    }
}